<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/8.2.0/ol.min.css"
    integrity="sha512-bc9nJM5uKHN+wK7rtqMnzlGicwJBWR11SIDFJlYBe5fVOwjHGtXX8KMyYZ4sMgSL0CoUjo4GYgIBucOtqX/RUQ=="
    crossorigin="anonymous" referrerpolicy="no-referrer" />
  <script src="./assets/js/gifler.min.js"></script>
  <title>openlayers离散知识点</title>

  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    :root {
      --top-height: 50px;
    }

    html,
    body {
      height: 100%;
      height: 100%;
    }

    #container {
      width: 100%;
      height: 100%;
      overflow: hidden;
      position: absolute;
    }

    button {
      border-radius: 0;
      background-color: #07c160;
      border: none;
      outline: none;
      cursor: pointer;
      height: 40px;
      line-height: 40px;
      color: #fff;
      padding: 0 20px;
    }

    select {
      height: 40px;
      line-height: 40px;
      outline: none;
    }

    .ml10 {
      margin-left: 10px;
    }

    #app {
      width: 100vw;
      height: 100vh;
      display: flex;
    }

    .info,
    .app-map {
      height: 100vh;
      width: 50vw;
    }

    .info {
      padding: 30px;

    }

    .info>div {
      margin-bottom: 12px;
    }

    .log {
      max-height: 400px;
      overflow: auto;
    }

    .log div {
      height: 30px;
      line-height: 30px;
    }

    .distance,
    .msg {
      height: 40px;
      line-height: 40px;
      font-size: 16px;
    }

    .distance label {
      width: 50px;
      display: inline-block;
    }

    .distance select {
      width: 120px;
      margin-right: 12px;
    }

    .row {
      height: 50px;
    }
  </style>

</head>

<body>
  <div id="app">
    <div class="app-map" id="app-map"></div>
    <div class="info">
      <div class='msg'>openlayers 版本号: <b>{{version}}</b></div>
      <div class='msg'>地图展示层级: <b>{{level}}</b></div>
      <div class='msg'>地图投影方式: <b>{{epsg}}</b></div>
      <div class="distance">
        <label for="start">起点</label>
        <select name="start" id="start" v-model='start'>
          <option v-for='point in pointData' :key='point.name' :value="point.center.join(',')">{{point.name}}</option>
        </select>
        <label for="end">终点</label>
        <select name="end" id="end" v-model='end'>
          <option v-for='point in pointData' :key='point.name' :value="point.center.join(',')">{{point.name}}</option>
        </select>
        <button @click='handleClickCalcDistance'>计算距离</button>
        <span class="ml10"><b>{{distanceText}}</b></span>
      </div>
      <div class="row">
        <button @click='handleClickDrawCircle'>绘制圆</button>
        <span class="ml10" v-show='center !== ""'>圆的中心坐标: ({{center}})</span>
        <span class="ml10" v-show='radius !== ""'>圆的半径: {{radius}}米</span>
      </div>
      <div class="row">
        <button @click='handleClickTogglePointLayer'>{{this.pointLayerIsVisible ? '隐藏' : '显示'}}点位图层</button>
        <span class="ml10">点位图层状态： {{pointLayerIsVisible ? '显示着' : '隐藏着'}}</span>
      </div>
      <div class="row">
        <button @click='handleClickUpZIndex(2)'>提高点位图层层级</button>
        <button @click='handleClickUpZIndex(1)' class="ml10">提高多边形图层层级</button>
        <span class='index ml10'>点位图层层级:<b>{{pointZIndex}}</b></span>
        <span class='index ml10'>多边形图层层级:<b>{{polygonZIndex}}</b></span>
      </div>
      <div class="log">
        <div v-for='(log, index) in logs' :key='log'>{{index + 1}} {{log}}</div>
      </div>
    </div>
  </div>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/8.2.0/dist/ol.min.js"
    integrity="sha512-+nvfloZUX7awRy1yslYBsicmHKh/qFW5w79+AiGiNcbewg0nBy7AS4G3+aK/Rm+eGPOKlO3tLuVphMxFXeKeOQ=="
    crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.4.14/vue.global.prod.min.js"
    integrity="sha512-huEQFMCpBzGkSDSPVAeQFMfvWuQJWs09DslYxQ1xHeaCGQlBiky9KKZuXX7zfb0ytmgvfpTIKKAmlCZT94TAlQ=="
    crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <!-- <script src="https://cdn.jsdelivr.net/npm/gifler@0.1.0/gifler.min.js"></script> -->

  <script>
    const { createApp } = Vue;
    // feature图片
    const base64Img = '';
    // 基础样式style
    const baseStyle = new ol.style.Style({
      image: new ol.style.Icon({
        src: base64Img,
        scale: 1.5,
        anchor: [0.5, 0.5],
        rotateWithView: true,
        rotation: 0,
        opacity: 1
      }),
    });
    // 点位信息
    const pointData = [
      {
        name: '北京市',
        center: [116.405285, 39.904989]
      },
      {
        name: '天津市',
        center: [117.190182, 39.125596]
      },
      {
        name: '河北省',
        center: [114.502461, 38.045474]
      },
      {
        name: '山西省',
        center: [112.549248, 37.857014]
      },
      {
        name: '内蒙古自治区',
        center: [111.670801, 40.818311]
      },
      {
        name: '辽宁省',
        center: [123.429096, 41.796767]
      },
      {
        name: '吉林省',
        center: [125.3245, 43.886841]
      },
      {
        name: '黑龙江省',
        center: [126.642464, 45.756967]
      },
      {
        name: '上海市',
        center: [121.472644, 31.231706]
      },
      {
        name: '江苏省',
        center: [118.767413, 32.041544]
      },
      {
        name: '浙江省',
        center: [120.153576, 30.287459]
      },
      {
        name: '安徽省',
        center: [117.283042, 31.86119]
      },
      {
        name: '福建省',
        center: [119.306239, 26.075302]
      },
      {
        name: '江西省',
        center: [115.892151, 28.676493]
      },
      {
        name: '山东省',
        center: [117.000923, 36.675807]
      },
      {
        name: '河南省',
        center: [113.665412, 34.757975]
      },
      {
        name: '湖北省',
        center: [114.298572, 30.584355]
      },
      {
        name: '湖南省',
        center: [112.982279, 28.19409]
      },
      {
        name: '广东省',
        center: [113.280637, 23.125178]
      },
      {
        name: '广西壮族自治区',
        center: [108.320004, 22.82402]
      },
      {
        name: '海南省',
        center: [110.33119, 20.031971]
      },
      {
        name: '重庆市',
        center: [106.504962, 29.533155]
      },
      {
        name: '四川省',
        center: [104.065735, 30.659462]
      },
      {
        name: '贵州省',
        center: [106.713478, 26.578343]
      },
      {
        name: '云南省',
        center: [102.712251, 25.040609]
      },
      {
        name: '西藏自治区',
        center: [91.132212, 29.660361]
      },
      {
        name: '陕西省',
        center: [108.948024, 34.263161]
      },
      {
        name: '甘肃省',
        center: [103.823557, 36.058039]
      },
      {
        name: '青海省',
        center: [101.778916, 36.623178]
      },
      {
        name: '宁夏回族自治区',
        center: [106.278179, 38.46637]
      },
      {
        name: '新疆维吾尔自治区',
        center: [87.617733, 43.792818]
      },
      {
        name: '台湾省',
        center: [121.509062, 25.044332]
      },
      {
        name: '香港特别行政区',
        center: [114.173355, 22.320048]
      },
      {
        name: '澳门特别行政区',
        center: [113.54909, 22.198951]
      },
    ]
    function transform(e) {
      return ol.proj.transform(e, 'EPSG:4326', 'EPSG:3857');
    };
    const vm = createApp({
      data() {
        return {
          map: {},
          polygonLayer: {}, // 多边形图层
          pointLayer: {}, // 点位图层
          drawSource: {}, // 绘制图层的数据源
          center: '', // 圆的中心坐标
          radius: '', // 圆的半径
          version: ol.util.VERSION, // openlayers版本号
          epsg: '', // 当前地图的投影方式
          level: 4, // 当前地图层级
          logs: [], // 操作日志
          start: pointData[0].center.join(','), // 距离起始点
          end: pointData[1].center.join(','), // 距离结束点
          distanceText: '', // 计算出来的距离
          pointData, // 点位数据
          draw: null, // 绘制圆的工具
          pointLayerIsVisible: true, // 点位图层显示状态
          pointZIndex: 2, // 点位图层的zindex
          polygonZIndex: 2 // 多边形图层的zindex
        }
      },
      methods: {
        // 初始化地图
        initMap() {
          // 高德地图瓦片地址
          const vectorLayer = new ol.layer.Tile({
            source: new ol.source.XYZ({
              url: 'http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}'
            }),
            name: '初始化地图图层'
          });
          // 绘制圆形的图层
          this.drawSource = new ol.source.Vector();
          const drawLayer = new ol.layer.Vector({
            source: this.drawSource,
          });
          //  初始化地图
          this.map = new ol.Map({
            target: 'app-map',
            layers: [vectorLayer, drawLayer],
            view: new ol.View({
              projection: 'EPSG:3857',
              //设定中心点，因为默认坐标系为 3587，所以要将我们常用的经纬度坐标系4326 转换为 3587坐标系
              center: transform([111.8453154, 32.7383500]),
              zoom: 4,
            })
          });
          // 绑定地图事件
          this.bindMapEvt();
          // 渲染点位到地图上
          this.renderPoint();
          // 渲染多边形到地图
          this.renderPolygon();

          this.addGif();
        },
        // 绑定地图事件
        bindMapEvt() {
          // 点击事件
          this.map.on('click', (evt) => {
            const clickPoint = ol.proj.transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326')
            this.logs.push(`触发了点击事件，点击的经纬度是(${clickPoint[0].toFixed(7)}, ${clickPoint[1].toFixed(7)})`);
          });
          // 双击事件
          this.map.on('dblclick', (evt) => {
            evt.stopPropagation();
            const clickPoint = ol.proj.transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326')
            this.logs.push(`触发了双击事件，双击的经纬度是(${clickPoint[0].toFixed(7)}, ${clickPoint[1].toFixed(7)})`);
          });
          // 鼠标移动事件
          this.map.on('pointermove', (e) => {
            // 获取像素位置
            let pixel = this.map.getEventPixel(e.originalEvent);
            // 根据点位像素位置，获取此位置的要素feature
            let feature = this.map.forEachFeatureAtPixel(pixel, (feature) => {
              return feature;
            });
            // 要素存在，并且是需要改变鼠标样式为pointer的feature，鼠标样式变为pointer，否则auto
            if (feature && feature.get('pointer')) {
              console.log(`鼠标移动事件，鼠标移动到了点位上`);
              this.map.getTargetElement().style.cursor = 'pointer';
            } else {
              this.map.getTargetElement().style.cursor = 'auto';
            }
          });
          // 地图移动结束时间
          this.map.on('moveend', (e) => {
            const nlevel = this.map.getView().getZoom();
            if (nlevel === this.level) {
              this.logs.push(`移动结束事件，根据层级变化，判断为平移 ${nlevel} -> ${nlevel}`);
            } else {
              this.logs.push(`移动结束事件，根据层级变化，判断为缩放 ${this.level} -> ${nlevel}`);
              this.level = nlevel;
            }
          });
        },
        // 在地图上添加点位
        renderPoint(numPoints) {
          let that = this;
          const gifUrl = './assets/images/test.gif';
          const gif = gifler(gifUrl);
          // 根据positions创建一个新的数据源和要素数组，
          const features = [];
          pointData.forEach((item, index) => {
            features.push(this.addGif(item.center))
          });
          const vectorSource = new ol.source.Vector({
            // features: pointData.map(e => {
            //   // ol.proj.fromLonLat用于将经纬度坐标从 WGS84 坐标系转换为地图投影坐标系
            //   const feature = new ol.Feature({
            //     geometry: new ol.geom.Point(ol.proj.fromLonLat(e.center)),
            //     pointer: true
            //   });

            //   feature.setStyle(baseStyle);
            //   return feature;
            // })
            features: features,
          });
          // 创建带有数据源的矢量图层
          this.pointLayer = new ol.layer.Vector({
            source: vectorSource,
            layerID: 'addpointLayer'
          });
          // 将点位图层添加到地图上
          this.map.addLayer(this.pointLayer);
          this.pointLayer.setZIndex(this.pointZIndex);
          this.pointLayerIsVisible = this.pointLayer.getVisible();
        },
        // 在地图上添加一个多边形图层
        renderPolygon() {
          // 5个点的经纬度坐标
          const coordinates = [
            [116.405285, 39.904989],
            [87.617733, 43.792818],
            [91.132212, 29.660361],
            [110.33119, 20.031971],
            [116.405285, 39.904989]
          ];
          const coordinates3857 = coordinates.map(e => {
            return transform(e);
          });
          // 创建一个多边形要素
          const polygonFeature = new ol.Feature({
            geometry: new ol.geom.Polygon([coordinates3857]),
          });
          // 设置多边形的样式，填充颜色为绿色
          polygonFeature.setStyle(new ol.style.Style({
            fill: new ol.style.Fill({
              color: 'rgba(0, 255, 0, 0.8)' // 绿色填充，透明度为0.5
            }),
          }));
          // 创建一个矢量图层，并将多边形要素添加到图层中
          this.polygonLayer = new ol.layer.Vector({
            source: new ol.source.Vector({
              features: [polygonFeature]
            })
          });
          this.map.addLayer(this.polygonLayer);
          this.polygonLayer.setZIndex(this.polygonZIndex);
        },
        // 点击计算距离
        handleClickCalcDistance() {
          // baidumap.getDistance(new BMap.Point(116.405285, 39.904989),new BMap.Point(117.190182, 39.125596)) // 109744.28912948008
          const start = (this.start.split(',').map(e => parseFloat(e)));
          const end = (this.end.split(',').map(e => parseFloat(e)));
          this.distanceText = '距离是： ' + ol.sphere.getDistance(start, end) + '米';
          // 创建一条直线
          const lineString = new ol.geom.LineString([transform(start), transform(end)]);
          // 创建Feature
          const lineFeature = new ol.Feature(lineString);
          // 设置Feature样式
          lineFeature.setStyle(new ol.style.Style({
            stroke: new ol.style.Stroke({
              color: '#' + Math.random().toString(16).slice(2, 8),
              width: 3
            })
          }));
          // 将直线添加到图层
          this.polygonLayer.getSource().addFeature(lineFeature);
        },
        // 点击绘制圆
        handleClickDrawCircle() {
          const { map } = this;
          // 删除上一种绘制
          this.draw && map.removeInteraction(this.draw);
          // 根据 type 创建交互
          this.draw = new ol.interaction.Draw({
            source: this.drawSource,
            type: 'Circle'
          });
          // 将交互添加到地图
          map.addInteraction(this.draw);
          // 监听绘制完成的事件
          this.draw.on('drawend', (event) => {
            // 获取绘制的feature
            const feature = event.feature;
            const center = ol.proj.transform(feature.getGeometry().getCenter(), 'EPSG:3857', 'EPSG:4326');
            const radius = feature.getGeometry().getRadius();
            this.logs.push(`绘制的圆形的中心经纬度是：(${center[0]}, ${center[1]})`)
            this.logs.push(`绘制的圆形的半径是：${radius}，投影方式是 EPSG:3857，返回的单位是米`);
            this.center = center.map(e => e.toFixed(6)).join(',')
            this.radius = radius.toFixed(6);
            this.map.removeInteraction(this.draw);
          });
        },
        // 切换点位图层的显示隐藏
        handleClickTogglePointLayer() {
          this.pointLayer.setVisible(!this.pointLayerIsVisible);
          this.pointLayerIsVisible = this.pointLayer.getVisible();
        },
        // 提高图层的zindex
        handleClickUpZIndex(type) {
          const layer = type === 1 ? this.polygonLayer : this.pointLayer;
          const text = type === 1 ? '多边形' : '点位';
          let index = layer.getZIndex();
          layer.setZIndex(index + 1);
          type === 1 && (this.polygonZIndex = layer.getZIndex());
          type === 2 && (this.pointZIndex = layer.getZIndex());
          this.logs.push(text + '图层zindex设置为' + (index + 1));
        },
        addGif(center = [102.065735, 30.159462]) {
          const iconFeature = new ol.Feature({
            geometry: new ol.geom.Point(ol.proj.fromLonLat(center)),
            // geometry: new ol.geom.Point(transform([111.8453154, 32.7383500])),
            pointer: true,
          })

          // const vectorSource = new ol.source.Vector({
          //   features: [iconFeature]
          // })

          // const vectorLayer = new ol.layer.Vector({
          //   source: vectorSource,
          //   zIndex: 9,
          // })

          const gifUrl = './assets/images/test.gif';
          // const gif = gifler(gifUrl);

          gifler(gifUrl).frames(
            document.createElement('canvas'),
            (ctx, frame) => {
              iconFeature.setStyle(
                new ol.style.Style({
                  image: new ol.style.Icon({
                    img: ctx.canvas,
                    imgSize: [frame.width, frame.height],
                    opacity: 0.8,
                    scale: 0.2
                  })
                })
              )
              ctx.clearRect(0, 0, frame.width, frame.height)
              ctx.drawImage(frame.buffer, frame.x, frame.y)
              this.map.render()
            },
            true
          )
          // this.map.addLayer(vectorLayer)

          return iconFeature;
        },
      },
      mounted() {
        this.initMap();
        this.epsg = this.map.getView().getProjection().getCode();
      }
    }).mount('#app')
  </script>
</body>

</html>